package mth.rmclusterer;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class ReferenceComparator {
	/*
	 * Mit Hilfe der ReferenceComparator Klasse werden die Kennzahlen F-Measure und HitRate
	 * berechnet. Dazu werden Vergleiche mit den Referenzdaten verwendet, wofür ebenfalls
	 * eine Methode zur Verfügung steht.
	 */
	private static final String VALIDATION_FILE=Manager.VALIDATION_FILE;
	private String[] refCluster;
	private String[][] comparationClusters;
	private int[][] matchRefCluster;
	private double[] hitRate;
	private double[] f_measure;
	private String[] qValues;
	private String[] distMeasures;
	private String[] kValues;
	private String[] realQValue;
	private String[] nQValue;
	private String[] pairCount;
	
	
	ReferenceComparator(VotedRunResult[] vrr) {
		refCluster = new String[0];
		if (vrr != null && vrr.length!=0) {
			comparationClusters = new String[vrr.length][vrr[0].getAssignmentMix().length];
			matchRefCluster = new int[vrr.length][vrr[0].getAssignmentMix().length];
			hitRate = new double[vrr.length];
			f_measure = new double[vrr.length];
			qValues = new String[vrr.length];
			distMeasures = new String[vrr.length];
			kValues = new String[vrr.length];
			realQValue = new String[vrr.length];
			nQValue = new String[vrr.length];
			pairCount = new String[vrr.length];
			
			for (int a=0; a<vrr.length; a++) {
				qValues[a]=vrr[a].getQValue();
				distMeasures[a]=vrr[a].getDistanceMeasure();
				kValues[a]=vrr[a].getKValue();
				realQValue[a]=vrr[a].getRealQValue();
				nQValue[a]=vrr[a].getNQValue();
				pairCount[a]=vrr[a].getPairCount();
			}
		} else {
			comparationClusters = new String[0][0];
			matchRefCluster = new int[0][0];
			qValues = new String[0];
			distMeasures = new String[0];
			realQValue = new String[0];
			nQValue = new String[0];
			pairCount = new String[0];
		}
		
		BufferedReader lineRider;
		try {
			lineRider = new BufferedReader(new FileReader(VALIDATION_FILE));
			String element=lineRider.readLine();
			element=lineRider.readLine(); //first row contains attribute names
			while (element!=null) {
				refCluster=Manager.addStringToArray(refCluster, "cluster_"+element.split(",")[element.split(",").length-1]);
				element = lineRider.readLine();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		for (int a=0; a<vrr.length; a++) {
			for (int b=0; b<vrr[a].getAssignmentMix().length; b++) {
				comparationClusters[a][b]=vrr[a].getAssignmentMix()[b];
			}
		}
	}
	
	private double calculateFMeasure(Map<String, Integer> r, Map<String, Integer> c, String[] distRefClu, String[] distCluClu, int v) {
		double[] tmpFMeasure=new double[c.size()];
		for (int a=0; a<r.size(); a++) {
			for (int b=0; b<c.size(); b++) {
				double precision = calculatePrecision(c.get(distCluClu[b]), distCluClu[b],v);
				double recall = calculateRecall(r.get(distCluClu[b]), distCluClu[b],v);
				if (precision==0.0 && recall==0.0) {
					tmpFMeasure[b]=0.0;
				} else {
					tmpFMeasure[b]=(tmpFMeasure[b]>=(2*recall*precision)/(recall+precision))?tmpFMeasure[b]:(2*recall*precision)/(recall+precision);
				}
				
			}
		}
		
		double retFMeasure=0.0;
		for (int a=0; a<r.size(); a++) {
			if (tmpFMeasure.length<r.size() && a>=tmpFMeasure.length) {
				retFMeasure+=0;
			} else {
				retFMeasure+=(double)((double)r.get(distRefClu[a])/(double)comparationClusters[0].length)*tmpFMeasure[a];
			}
		}
		
		return retFMeasure;
	}
	
	private double calculatePrecision(int cluCount, String clu, int v) {
		int a_ij = 0;
		for (int a=0; a<comparationClusters[v].length; a++) {
			if (refCluster[a].equals(comparationClusters[v][a]) && refCluster[a].equals(clu)) {
				a_ij++;
			}
		}
		return (double)((double)a_ij/(double)cluCount);
	}
	
	private double calculateRecall(int refCount, String clu, int v) {
		int a_ij = 0;
		for (int a=0; a<comparationClusters[v].length; a++) {
			if (refCluster[a].equals(comparationClusters[v][a]) && refCluster[a].equals(clu)) {
				a_ij++;
			}
		}
		return (double)((double)a_ij/(double)refCount);
	}
	
	private double computeSingleHitRate(int v) {
		int retVal=0;
		for (int a=0; a<matchRefCluster[v].length; a++) {
			retVal+=matchRefCluster[v][a];
		}
		return (double)((double)retVal/(double)matchRefCluster[v].length);
	}
	
	private void calculateHitRate() {
		for (int a=0; a<comparationClusters.length; a++) {
			hitRate[a]=computeSingleHitRate(a);
		}
	}
	
	public double[] getHitRate() {
		return hitRate;
	}
	
	public double[] getFMeasure() {
		return f_measure;
	}
	
	public void matchWithReference() {
		int[] ref=new int[0];
		String[] distinctRefClusters = new String[0];
		int[] clu=new int[0];
		String[] distinctCluClusters = new String[0];
		Map <String, Integer> r = new LinkedHashMap<String, Integer>();
		Map <String, Integer> cl = new LinkedHashMap<String, Integer>();
		
		for (int a=0; a<comparationClusters.length; a++) {
			for (int b=0; b<comparationClusters[a].length; b++) {
				if (comparationClusters[a][b].equals(refCluster[b])) {
					matchRefCluster[a][b]=1;
				}
				if (!Manager.stringArrayContains(distinctRefClusters, refCluster[b])) {
					distinctRefClusters=Manager.addStringToArray(distinctRefClusters, refCluster[b]);
					ref=Manager.addIntegerToArray(ref, 1);
				} else {
					boolean breaker=false;
					for (int c=0; c<distinctRefClusters.length && !breaker; c++) {
						if (distinctRefClusters[c].equals(refCluster[b])) {
							ref[c]++;
							breaker=true;
						}
					}
				}
				if (!Manager.stringArrayContains(distinctCluClusters, comparationClusters[a][b])) {
					distinctCluClusters=Manager.addStringToArray(distinctCluClusters, comparationClusters[a][b]);
					clu=Manager.addIntegerToArray(clu, 1);
				} else {
					boolean breaker=false;
					for (int c=0; c<distinctCluClusters.length && !breaker; c++) {
						if (distinctCluClusters[c].equals(comparationClusters[a][b])) {
							clu[c]++;
							breaker=true;
						}
					}
				}
			}
			for (int b=0; b<distinctRefClusters.length; b++) {
				r.put(distinctRefClusters[b],ref[b]);
			}
			for (int b=0; b<distinctCluClusters.length; b++) {
				cl.put(distinctCluClusters[b], clu[b]);
			}
			f_measure[a] = calculateFMeasure(r, cl, distinctRefClusters, distinctCluClusters, a);
			r.clear();
			cl.clear();
			clu = new int[0];
			ref = new int[0];
			distinctCluClusters = new String[0];
			distinctRefClusters = new String[0];
		}
		calculateHitRate();
	}
}
